<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>08.Materials(MeshLambertMaterial)</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <script id="vertexShader" type="x-shader/x-vertex">
        attribute vec3 normal;
        attribute vec3 position;
        attribute vec4 color;
        uniform vec3 camera;
        uniform vec3 light;
        uniform mat3 normalMatrix;
        uniform mat4 viewMatrix;
        uniform mat4 modelMatrix;
        uniform mat4 modelViewMatrix; // optional
        uniform mat4 projectionMatrix; // optional

        varying vec3 varNormal;
        varying vec3 varEyeDir;
        varying vec3 lightDirection;
        void main()
        {
            
            // vertex position (in eye space)
            vec4 eyePosition=modelViewMatrix * vec4( position, 1.0 );
            vec4 eyeCamera=viewMatrix*vec4(camera,1.0);
            vec4 eyeLight=viewMatrix*vec4(light,1.0);
            // vertex normal 
            varNormal =  normalMatrix*normal;
            
            varEyeDir =  normalize(eyeCamera.xyz-eyePosition.xyz);
            lightDirection=normalize(eyeLight.xyz-eyePosition.xyz);
            gl_Position = projectionMatrix * eyePosition;

        } 

    </script>

    <script id="fragmentShader" type="x-shader/x-fragment">
        precision highp float; 
        varying vec3 lightDirection;
        varying vec3 varNormal;
        varying vec3 varEyeDir;
        uniform vec3 lightColor;
        void main()
        {
            // set important material values
            float roughnessValue = 0.3; // 0 : smooth, 1: rough
            float F0 = 0.8; // fresnel reflectance at normal incidence
            float k = 0.2; // fraction of diffuse reflection (specular reflection = 1 - k)
      
            
            // interpolating normals will change the length of the normal, so renormalize the normal.
            vec3 normal = normalize(varNormal);
            
            // do the lighting calculation for each fragment.
            float NdotL = max(dot(normal, lightDirection), 0.0);
            
            float specular = 0.0;
            if(NdotL > 0.0)
            {
                vec3 eyeDir = normalize(varEyeDir);
        
                // calculate intermediary values
                vec3 halfVector = normalize(lightDirection + eyeDir);
                float NdotH = max(dot(normal, halfVector), 0.0); 
                float NdotV = max(dot(normal, eyeDir), 0.0); // note: this could also be NdotL, which is the same value
                float VdotH = max(dot(eyeDir, halfVector), 0.0);
                float mSquared = roughnessValue * roughnessValue;
                
                // geometric attenuation
                float NH2 = 2.0 * NdotH;
                float g1 = (NH2 * NdotV) / VdotH;
                float g2 = (NH2 * NdotL) / VdotH;
                float geoAtt = min(1.0, min(g1, g2));
            
                // roughness (or: microfacet distribution function)
                // beckmann distribution function
                float r1 = 1.0 / ( 4.0 * mSquared * pow(NdotH, 4.0));
                float r2 = (NdotH * NdotH - 1.0) / (mSquared * NdotH * NdotH);
                float roughness = r1 * exp(r2);
                
                // fresnel
                // Schlick approximation
                float fresnel = pow(1.0 - VdotH, 5.0);
                fresnel *= (1.0 - F0);
                fresnel += F0;
                
                specular = (fresnel * geoAtt * roughness) / (NdotV * NdotL * 3.14);
            }
            
            vec3 finalValue = lightColor * NdotL * (k + specular * (1.0 - k));
            gl_FragColor = vec4(finalValue, 1.0);
        }



    </script>
    <script type="module">
        import * as THREE from '/build/three.module.js';
        import { PointerLockControls } from '/jsm/controls/PointerLockControls.js';
        //生成场景
        const scene = new THREE.Scene();
        //设置光照
        const light = new THREE.AmbientLight(0xffffff);
        scene.add(light);
        const pointLight = new THREE.PointLight(0xFFFFFF, 1, 100);
        pointLight.position.set(0, 0, 10);
        scene.add(pointLight);
    
        //设置相机
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.z = 5;
        //设置渲染器
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        //renderer.setClearColor(0xFFFFFF);
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        //生成模型
        const geometry = new THREE.BoxGeometry();

        const group = new THREE.Group()
        const material = new THREE.RawShaderMaterial({

            uniforms: {
                camera: { value: camera.position },
                light: { value: pointLight.position },
                lightColor:{value:new THREE.Vector3(0.9, 0.1, 0.1) }
           
            },
            vertexShader: document.getElementById('vertexShader').textContent,
            fragmentShader: document.getElementById('fragmentShader').textContent,

        });

        const cube = new THREE.Mesh(geometry, material);

        const wireframeMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000, wireframe: true });
        const cubeWireframe = new THREE.Mesh(geometry, wireframeMaterial);
        //设置场景
        
        group.add(cube)
        group.add(cubeWireframe)
        scene.add(group);
        scene.add(camera)
        const controls = new PointerLockControls(camera, document.body);

        //点击屏幕触发lock
        document.onclick = function (event) {
            controls.lock();
        }
        //用键盘控制移动
        document.onkeydown = function (event) {
            switch (event.key) {  // 获取当前按下键盘键的编码
                //wsad移动
                case 'w':
                    controls.moveForward(0.5);
                    break;
                case 's':
                    controls.moveForward(-0.5);
                    break;
                case 'a':
                    controls.moveRight(- 0.5);
                    break;
                case 'd':
                    controls.moveRight(0.5);
                    break;

            }
        }
        function animate() {
            group.rotation.x+=0.01;
            renderer.render(scene, camera);
            requestAnimationFrame(animate);
            material.uniforms.camera.value = camera.position
            

        }
        //开始动画
        animate()
    </script>
</body>

</html>